/* -*- mode: c++ -*-
** button.cpp
** 
** Made by Kjetil Svarstad
** 
*/

#include "button.h"

// Functionality for the Button class

// Constructor
Button::Button (sc_module_name buttonName, int buttonNumber) : sc_module (buttonName)
{
  number = buttonNumber;
  light = false; // Light is off when button is created.
  SC_HAS_PROCESS (Button);
  SC_THREAD(main);
}

void Button::switchLight(bool b)
{
	light = b;
	printf("Button%i : Light is %s\n",number, (light ? "on" : "off"));
}

void Button::main()
{
	while(true)
	{
		wait(PUSH.posedge_event());
		busp->buttonPushed(number);
	}
}

buttonAdapter::buttonAdapter (sc_module_name name, int unique) : sc_module(name)
  {
    Address = unique; // The buttons address start from zero so addr = unique is correct.
    currentButton = unique;
    SC_HAS_PROCESS(buttonAdapter);
    SC_THREAD(main);
  }

void buttonAdapter::main()
  {
    // TODO: Enable reading by events/flags instead of the constant intervals.
    int data;
    cout << "ButtonAdapter::main" << endl;
    while(true)
    {
			wait(400, SC_MS);
			status = busp -> burst_read(currentButton, &data, currentButton*4, 1, false);
			if (status == SIMPLE_BUS_ERROR) cout << "BUS_ERROR AT READ" << endl;
			buttonp->switchLight(data); // This switches light every time, but shouldn't matter if off is switched off again anyway.
			
		}

  }

void buttonAdapter:: buttonPushed(int buttonAddress)
  {
		// Create packet
		//cout << "Button pushed" << endl;
		dataPacket.size = 3;
		dataPacket.id = currentButton;
		dataPacket.pushed = 1;
		//Transmit packet
		send(&dataPacket);
  }
  
 void buttonAdapter::send(struct buttonPacket* dataPacket)
 {
		// TODO: Writing a packet should be possible with one write with pointer to the struct.
		// TODO: Shorter bitwise operations?
		unsigned int freeloc; // Free loc is stored at address 44, the first addr in FastMem.
		unsigned int status_register; // status_register is at address 40.
		unsigned int time_release= 16;
		
		// TRANSMIT PUSHED PACKET
		status = busp -> burst_read(dataPacket->id, &freeloc,  44, 1, false);
		if (status == SIMPLE_BUS_ERROR) cout << "BUS_ERROR AT READ" << endl;
		// Out of bounds check. Note that this does not completely fill the memory, should use modulo to implement better solution.
		if( (freeloc >= 4080) || (freeloc < 48))
			freeloc = 48; 
		// Write datapacket to FastMem
		//cout << "Freeloc1 dcimal = "  << freeloc << endl;
		status = busp-> burst_write(dataPacket->id,&(dataPacket->size), freeloc, 1, false);
		if (status == SIMPLE_BUS_ERROR) cout << "BUS_ERROR AT WRITE" << endl;
		freeloc+=4;
		status = busp-> burst_write(dataPacket->id,&(dataPacket->id), freeloc, 1, false);
		if (status == SIMPLE_BUS_ERROR) cout << "BUS_ERROR AT WRITE" << endl;
		freeloc+=4;
		status = busp-> burst_write(dataPacket->id,&(dataPacket->pushed), freeloc, 1, false);
		if (status == SIMPLE_BUS_ERROR) cout << "BUS_ERROR AT WRITE" << endl;
		// Update and write freeloc to FastMem
		freeloc+=4;
		status = busp -> burst_write(dataPacket->id, &freeloc, 44, 1 , false);
		if (status == SIMPLE_BUS_ERROR) cout << "BUS_ERROR AT WRITE" << endl;
		// Check status register of control module
		status = busp -> burst_read(dataPacket->id, &status_register, 40, 1, false);
		// Check first 16 bits for active buttons and wait if busy
		while((status_register & 0x00001111) != 0)
		{
			status = busp -> burst_read(dataPacket->id, &status_register, 40, 1, false);
			if (status == SIMPLE_BUS_ERROR) cout << "BUS_ERROR AT READ" << endl;
			wait(1,SC_MS);
		}
		// Need to specify bit here, status register is zero at this point
		status_register = 1;
		status_register <<= dataPacket->id; // Shift the 1 ,id spaces, to the left.
		freeloc -= 12; // The address of dataPacket
		//cout << "Freeloc1 dcimal2 = "  << freeloc << endl;
		freeloc <<= 16; // Shift it to move the value to the first two bytes of the int.
		freeloc |= status_register; // Bitwise or with status_register to make a 4 byte word.
		//cout << "Freeloc2 hex = " << hex << freeloc << endl;
		status = busp -> burst_write(dataPacket->id, &freeloc, 40, 1, false); 
		if (status == SIMPLE_BUS_ERROR) cout << "BUS_ERROR AT WRITE" << endl;
		
		
		// TRANSMIT RELEASE PACKET -  Writing it to the next available freeloc
		wait(time_release, SC_MS);
		send_release_packet(dataPacket,  freeloc);
		
		// TASK 5 - Extra release packets
		unsigned int rand_time;
		unsigned int noise_time = 0;
		// Subtract time_release to avoid sending packets after 500ms, cause a button might have been pushed.
		while(noise_time < (500-(4*time_release))) 
		{
		  dataPacket->size = rand() % 17 + 3; //random packetsize between 3 and 20 words
		  rand_time = (rand() % 4) * time_release; // random time interval between T_r and 4T_r
		  noise_time += rand_time;
		  wait(rand_time, SC_MS);
		  send_release_packet(dataPacket, freeloc);
		}		
 }
 
 void buttonAdapter::send_release_packet(struct buttonPacket* dataPacket, unsigned int freeloc)
 {
		unsigned int status_register;
		dataPacket->pushed = 0; // Button released
		// Get freeloc - check for out of bounds - write packet and update freeloc
		status = busp -> burst_read(dataPacket->id, &freeloc,  44, 1, false);
		if (status == SIMPLE_BUS_ERROR) cout << "BUS_ERROR AT READ" << endl;
		// Out of bounds check. Note that this does not completely fill the memory, should use modulo to implement better solution.
		if((freeloc + 60) >= 4092)
			freeloc = 48; 
		status = busp-> burst_write(dataPacket->id,&(dataPacket->size), freeloc, 1, false);
		if (status == SIMPLE_BUS_ERROR) cout << "BUS_ERROR AT WRITE" << endl;
		freeloc+=4;
		status = busp-> burst_write(dataPacket->id,&(dataPacket->id), freeloc, 1, false);
		if (status == SIMPLE_BUS_ERROR) cout << "BUS_ERROR AT WRITE" << endl;
		freeloc+=4;
		status = busp-> burst_write(dataPacket->id,&(dataPacket->pushed), freeloc, 1, false);
		if (status == SIMPLE_BUS_ERROR) cout << "BUS_ERROR AT WRITE" << endl;
		// Add extra to freeloc here if size is big(task 5)
		freeloc+=4;
		freeloc+=(4*(dataPacket->size)) - 12;
		status = busp -> burst_write(dataPacket->id, &freeloc, 44, 1 , false);
		if (status == SIMPLE_BUS_ERROR) cout << "BUS_ERROR AT WRITE" << endl;
		// Check status register of control module
		status = busp -> burst_read(dataPacket->id, &status_register, 40, 1, false);
		if (status == SIMPLE_BUS_ERROR) cout << "BUS_ERROR AT READ" << endl;
		// Check last 16 bits for active buttons and wait if busy
		while((status_register & 0x00001111) != 0)
		{
			status = busp -> burst_read(dataPacket->id, &status_register, 40, 1, false);
			if (status == SIMPLE_BUS_ERROR) cout << "BUS_ERROR AT READ" << endl;
			wait(1,SC_MS);
		}
		// Need to specify bit here, status register is zero at this point
		status_register = 1;
		status_register <<= dataPacket->id; // Shift the 1 ,id spaces, to the left.
		freeloc -= 12; // The address of dataPacket
		freeloc <<= 16; // Shift it to move the value to the first two bytes of the int.
		freeloc |= status_register; // Bitwise or with status_register to make a 4 byte word.
		status = busp -> burst_write(dataPacket->id, &freeloc, 40, 1, false);
 }
